home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-03-26 | 67.8 KB | 2,794 lines |
- The following shar contains new versions of files that add compatibility with
- ISC 2.2 with TCP for the NNTP server. This is an UNOFFICAL patch. Please
- define LAI_TCP in common/conf.h to make use of these modifications. They
- will be available on the "archive-server@bcm.tmc.edu" as nntp.isc.shar in
- the public directory.
-
- #!/bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # server/timer.c
- # server/subnet.c
- # xmit/nntpxmit.c
- # xmit/remote.c
- # xfer/nntpxfer.c
- # This archive created: Tue Mar 26 09:40:27 1991
- :
- export PATH; PATH=/bin:$PATH
- if test -d 'server'
- then
- echo "Entering directory 'server'"
- cd 'server'
- echo shar: extracting "'timer.c'" '(4008 characters)'
- sed 's/^ X//' << \SHAR_EOF > 'timer.c'
- X/*
- X * Machinery to run routines off timers.
- X */
- X#include "common.h"
- X
- X#ifdef TIMERS
- X#ifndef lint
- Xstatic char rcsid[] =
- X "@(#) $Header: timer.c,v 1.3 91/03/19 03:02:41 sob Exp $ (NNTP with TIMERS)";
- X#endif
- X#else
- X#ifndef lint
- Xstatic char rcsid[] =
- X "@(#) $Header: timer.c,v 1.3 91/03/19 03:02:41 sob Exp $ (NNTP without TIMERS)";
- X#endif
- X#endif
- X
- X#ifdef TIMERS
- X#include <sys/time.h>
- X#include "timer.h"
- X#ifdef USG
- X#ifdef LAI_TCP
- X#include <sys/bsdtypes.h>
- X#define BSDSELECT
- X#endif
- X#else
- X#ifndef FD_SETSIZE
- X/* Forward compatability */
- X#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
- X#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
- X#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
- X#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
- X#define BSDSELECT
- X#endif
- X#endif
- X/* non-portable */
- X#define BUFFERED_DATA(f) ((f)->_cnt > 0)
- X
- Xstatic long lastsecs;
- X
- X/*
- X * Should be called before first call to timer_sleep()
- X */
- Xvoid
- Xtimer_init(timers, ntimer)
- X register struct timer *timers;
- X register int ntimer;
- X{
- X register int i;
- X register struct timer *tp;
- X
- X#ifdef SYSLOG
- X if (ntimer <= 0)
- X syslog(LOG_ERR,
- X "timer_init(): configuration error, %d timers\n", ntimer);
- X#endif
- X
- X /* Reset all timers */
- X for (i = ntimer, tp = timers; i > 0; --i, ++tp)
- X tp->left = tp->seconds;
- X
- X /* Start clock */
- X lastsecs = time((long *)0);
- X}
- X
- X/*
- X * Sleep until input or next timer needs to be run and then run any
- X * expired timers. Returns true if input is available to be read.
- X */
- Xint
- Xtimer_sleep(timers, ntimer)
- X register struct timer *timers;
- X register int ntimer;
- X{
- X register int i, n;
- X register struct timer *tp;
- X register long secs;
- X#ifndef BSDSELECT
- X long timeout;
- X long readfds;
- X#else
- X register struct timeval *timeoutp;
- X struct timeval timeout;
- X fd_set readfds;
- X#endif
- X
- X /* No need to do the select if there are characters in the buffer */
- X if (BUFFERED_DATA(stdin))
- X return(1);
- X
- X /* Length of next timeout is minimum of all "timers" */
- X#ifndef BSDSELECT
- X timeout = -1;
- X for (i = ntimer, tp = timers; i > 0; --i, ++tp)
- X if (tp->left >= 0 &&
- X (tp->left < timeout || timeout < 0))
- X timeout = tp->left;
- X
- X /* If active timeouts (this can easily happen), block until input */
- X if (timeout < 0)
- X timeout = 0;
- X#ifdef EXCELAN
- X readfds = 1<<(fileno(stdin));
- X timeout = timeout * 1000; /* timeout needs to be in milliseconds */
- X#endif /* EXCELAN */
- X#else
- X timeout.tv_sec = -1;
- X timeout.tv_usec = 0;
- X for (i = ntimer, tp = timers; i > 0; --i, ++tp)
- X if (tp->left >= 0 &&
- X (tp->left < timeout.tv_sec || timeout.tv_sec < 0))
- X timeout.tv_sec = tp->left;
- X
- X /* If active timeouts (this can easily happen), block until input */
- X if (timeout.tv_sec < 0)
- X timeoutp = 0;
- X else
- X timeoutp = &timeout;
- X
- X /* Do select */
- X FD_ZERO(&readfds);
- X FD_SET(fileno(stdin), &readfds);
- X#endif /* BSDSELECT */
- X errno = 0;
- X#ifdef EXCELAN
- X n = select(fileno(stdin) + 1, &readfds, (long*)0, timeout);
- X#else
- X n = select(fileno(stdin) + 1,
- X &readfds, (fd_set*)0, (fd_set*)0, timeoutp);
- X#endif
- X /* "Interrupted system call" isn't a real error */
- X if (n < 0 && errno != EINTR) {
- X#ifdef SYSLOG
- X syslog(LOG_ERR, "%s read select: %m", hostname);
- X#endif
- X exit(1);
- X }
- X
- X /* Calculate off seconds since last time */
- X secs = time((long *)0) - lastsecs;
- X if (secs < 0)
- X secs = 0;
- X
- X /* Subtract time from "timers" that have time remaining */
- X for (i = ntimer, tp = timers; i > 0; --i, ++tp)
- X if (tp->left > 0 && (tp->left -= secs) < 0)
- X tp->left = 0;
- X
- X /* Update lastsecs */
- X lastsecs += secs;
- X
- X /* If we have input, reset clock on guys that like it that way */
- X if (n > 0)
- X for (i = ntimer, tp = timers; i > 0; --i, ++tp)
- X if (tp->resetoninput)
- X tp->left = tp->seconds;
- X
- X /* Process "timers" that have timed out */
- X for (i = ntimer, tp = timers; i > 0; --i, ++tp) {
- X if (tp->left == 0) {
- X (tp->subr)();
- X /* resetoninput guys only get "reset on input" */
- X if (tp->resetoninput)
- X tp->left = -1;
- X else
- X tp->left = tp->seconds;
- X }
- X }
- X
- X /* Indicate no input */
- X if (n <= 0)
- X return(0);
- X return(1);
- X
- X}
- X#endif
- SHAR_EOF
- if test 4008 -ne "`wc -c < 'timer.c'`"
- then
- echo shar: error transmitting "'timer.c'" '(should have been 4008 characters)'
- fi
- echo shar: extracting "'subnet.c'" '(6645 characters)'
- sed 's/^ X//' << \SHAR_EOF > 'subnet.c'
- X#ifndef lint
- Xstatic char *sccsid = "@(#)$Header: subnet.c,v 1.9 91/03/19 03:02:30 sob Exp $";
- X#endif
- X
- X#include "../common/conf.h"
- X
- X#ifdef SUBNET
- X
- X#include <sys/types.h>
- X#ifdef LAI_TCP
- X#include <sys/bsdtypes.h>
- X#include <sys/stream.h>
- X#endif
- X#include <sys/socket.h>
- X#include <netinet/in.h>
- X#ifndef NETMASK
- X#include <net/if.h>
- X#endif
- X#ifdef LAI_TCP
- X#include <sys/sioctl.h>
- X#else
- X#include <sys/ioctl.h>
- X#endif
- X/*
- X * The following routines provide a general interface for
- X * subnet support. Like the library function "inet_netof",
- X * which returns the standard (i.e., non-subnet) network
- X * portion of an internet address, "inet_snetof" returns
- X * the subnetwork portion -- if there is one. If there
- X * isn't, it returns 0.
- X *
- X * Subnets, under 4.3, are specific to a given set of
- X * machines -- right down to the network interfaces.
- X * Because of this, the function "getifconf" must be
- X * called first. This routine builds a table listing
- X * all the (internet) interfaces present on a machine,
- X * along with their subnet masks. Then when inet_snetof
- X * is called, it can quickly scan this table.
- X *
- X * Unfortunately, there "ain't no graceful way" to handle
- X * certain situations. For example, the kernel permits
- X * arbitrary subnet bits -- that is, you could have a
- X * 22 bit network field and a 10 bit subnet field.
- X * However, due to braindamage at the user level, in
- X * such sterling routines as getnetbyaddr, you need to
- X * have a subnet mask which is an even multiple of 8.
- X * Unless you are running with class C subnets, in which
- X * case it should be a multiple of 4. Because of this rot,
- X * if you have non-multiples of 4 bits of subnet, you should
- X * define DAMAGED_NETMASK when you compile. This will round
- X * things off to a multiple of 8 bits.
- X *
- X * Finally, you may want subnet support even if your system doesn't
- X * support the ioctls to get subnet mask information. If you want
- X * such a thing, you can define NETMASK to be a constant that is
- X * the subnet mask for your network.
- X *
- X * And don't *even* get me started on how the definitions of the inet_foo()
- X * routines changed between 4.2 and 4.3, making internet addresses
- X * be unsigned long vs. struct in_addr. Don't blame me if this
- X * won't lint...
- X */
- X
- X/*
- X * One structure for each interface, containing
- X * the network number and subnet mask, stored in HBO.
- X */
- Xstruct in_if {
- X u_long i_net; /* Network number, shifted right */
- X u_long i_subnetmask; /* Subnet mask for this if */
- X int i_bitshift; /* How many bits right for outside */
- X};
- X
- X/*
- X * Table (eventually, once we malloc) of
- X * internet interface subnet information.
- X */
- Xstatic struct in_if *in_ifsni;
- X
- Xstatic int if_count;
- X
- X/*
- X * Get the network interface configuration,
- X * and squirrel away the network numbers and
- X * subnet masks of each interface. Return
- X * number of interfaces found, or -1 on error.
- X * N.B.: don't call this more than once...
- X */
- X
- Xgetifconf()
- X{
- X#ifndef NETMASK
- X register int i, j;
- X int s;
- X struct ifconf ifc;
- X char buf[1024];
- X register struct ifreq *ifr;
- X int inet_netof();
- X u_long addr;
- X
- X /*
- X * Find out how many interfaces we have, and malloc
- X * room for information about each one.
- X */
- X
- X s = socket(AF_INET, SOCK_DGRAM, 0);
- X if (s < 0)
- X return (-1);
- X
- X ifc.ifc_buf = buf;
- X ifc.ifc_len = sizeof (buf);
- X
- X if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
- X (void) close(s);
- X return (-1);
- X }
- X
- X /*
- X * if_count here is the count of possible
- X * interfaces we may be interested in... actual
- X * interfaces may be less (some may not be internet,
- X * not all are necessarily up, etc.)
- X */
- X
- X if_count = ifc.ifc_len / sizeof (struct ifreq);
- X
- X in_ifsni = (struct in_if *)
- X malloc((unsigned) if_count * sizeof (struct in_if));
- X if (in_ifsni == 0) {
- X (void) close(s);
- X return (-1);
- X }
- X
- X for (i = j = 0; i < if_count; ++i) {
- X struct sockaddr_in *s_in;
- X
- X ifr = &ifc.ifc_req[i];
- X if (ioctl(s, SIOCGIFFLAGS, ifr) < 0)
- X continue;
- X if ((ifr->ifr_flags & IFF_UP) == 0)
- X continue;
- X if (ioctl(s, SIOCGIFADDR, ifr) < 0)
- X continue;
- X if (ifr->ifr_addr.sa_family != AF_INET)
- X continue;
- X s_in = (struct sockaddr_in *) &ifr->ifr_addr;
- X addr = s_in->sin_addr.s_addr;
- X in_ifsni[j].i_net = inet_netof(s_in->sin_addr);
- X if (ioctl(s, SIOCGIFNETMASK, ifr) < 0)
- X continue;
- X s_in = (struct sockaddr_in *) &ifr->ifr_addr;
- X in_ifsni[j].i_subnetmask = ntohl(s_in->sin_addr.s_addr);
- X /*
- X * The following should "never happen". But under SunOS
- X * 3.4, along with the rest of their broken networking code,
- X * SIOCGIFNETMASK can get a netmask which is 0. There
- X * really isn't anything that "right" that we can do
- X * about it, so we'll set their subnet mask to be their
- X * *net*work mask. Which may or may not be right.
- X */
- X if (in_ifsni[j].i_subnetmask == 0) {
- X addr = ntohl(addr);
- X if (IN_CLASSA(addr))
- X in_ifsni[j].i_subnetmask = IN_CLASSA_NET;
- X else if (IN_CLASSB(addr))
- X in_ifsni[j].i_subnetmask = IN_CLASSB_NET;
- X else if (IN_CLASSC(addr))
- X in_ifsni[j].i_subnetmask = IN_CLASSC_NET;
- X else /* what to do ... */
- X in_ifsni[j].i_subnetmask = IN_CLASSC_NET;
- X } else
- X in_ifsni[j].i_bitshift = bsr(in_ifsni[j].i_subnetmask);
- X j++;
- X }
- X
- X if_count = j;
- X
- X (void) close(s);
- X
- X return (if_count);
- X
- X#else /* hard-coded subnets */
- X
- X if_count = 1;
- X
- X in_ifsni = (struct in_if *) malloc(if_count * sizeof (struct in_if));
- X if (in_ifsni == 0) {
- X return (-1);
- X }
- X in_ifsni[0].i_net = 0;
- X in_ifsni[0].i_subnetmask = NETMASK;
- X in_ifsni[0].i_bitshift = bsr(in_ifsni[0].i_subnetmask);
- X return (if_count);
- X#endif
- X}
- X
- X
- X/*
- X * Return the (sub)network number from an internet address.
- X * "in" is in NBO, return value in host byte order.
- X * If "in" is not a subnet, return 0.
- X */
- X
- Xu_long
- Xinet_snetof(in)
- X u_long in;
- X{
- X register int j;
- X register u_long i = ntohl(in);
- X register u_long net;
- X int inet_netof(), inet_lnaof();
- X struct in_addr in_a;
- X
- X in_a.s_addr = in;
- X net = inet_netof(in_a);
- X
- X /*
- X * Check whether network is a subnet;
- X * if so, return subnet number.
- X */
- X for (j = 0; j < if_count; ++j)
- X#ifdef NETMASK
- X if (1) {
- X#else
- X if (net == in_ifsni[j].i_net) {
- X#endif
- X net = i & in_ifsni[j].i_subnetmask;
- X in_a.s_addr = htonl(net);
- X if (inet_lnaof(in_a) == 0)
- X return (0);
- X else
- X return (net >> in_ifsni[j].i_bitshift);
- X }
- X
- X return (0);
- X}
- X
- X
- X/*
- X * Return the number of bits required to
- X * shift right a mask into a getnetent-able entity.
- X */
- X
- Xbsr(mask)
- X register long mask;
- X{
- X register int count = 0;
- X
- X if (mask == 0) /* "never happen", except with SunOS 3.4 */
- X return (0);
- X
- X while ((mask & 1) == 0) {
- X ++count;
- X mask >>= 1;
- X }
- X#ifdef DAMAGED_NETMASK
- X count /= 8; /* XXX gag retch puke barf */
- X count *= 8;
- X#endif
- X return (count);
- X}
- X
- X#endif
- SHAR_EOF
- if test 6645 -ne "`wc -c < 'subnet.c'`"
- then
- echo shar: error transmitting "'subnet.c'" '(should have been 6645 characters)'
- fi
- echo "Done with directory 'server'"
- cd ..
- fi
- if test -d 'xmit'
- then
- echo "Entering directory 'xmit'"
- cd xmit
- echo shar: extracting "'nntpxmit.c'" '(27564 characters)'
- sed 's/^ X//' << \SHAR_EOF > 'nntpxmit.c'
- X#ifndef lint
- Xstatic char * rcsid = "@(#)$Header: nntpxmit.c,v 1.7 91/03/19 03:03:16 sob Exp $";
- X#endif
- X/* nntpxmit - transmit netnews articles across the internet with nntp
- X**
- X** This program is for transmitting netnews articles between sites
- X** that offer the NNTP service, internet style. There are two forms
- X** of article transmission that can be used in this environment, since
- X** the communication is interactive (and relatively more immediate,
- X** when compared to batched file transfer protocols, like UUCP). They
- X** are: active send (I have `x', do you want it?) and polling (what
- X** have you gotten lately?).
- X**
- X** A C T I V E S E N D
- X**
- X** Sites on the UUCP network generally use active send, without asking
- X** in advance (that is, unless you got an article from your neighbor,
- X** or their site is listed in the Path: header already, you assume
- X** they don't have it and send it along). There is an ihave/sendme
- X** protocol for doing active send over batched links, but I claim that
- X** it won't work well because of the high latency between queueing
- X** and actual transfer that UUCP links typically have. That is, you'll
- X** still end up with a high rate of duplicate articles being sent over
- X** that type of link.
- X**
- X** With NNTP-based IHAVE, the update window in which another site can
- X** give the remote the article you just offered him is the time between
- X** the remote telling you it doesn't have the article, and your
- X** completed transfer of the article (pretty small). In practice, we
- X** still get duplicates, but generally from two problems: synchronized
- X** transmission of an article from two different neighbors (this can
- X** only be fixed by putting inews(1) into nntpd), and by articles
- X** being accepting during an expire(1) run (expire locks out inews
- X** processing while it is running, and articles collect until expire
- X** is done; since accepted article message-ids aren't added to
- X** the history file until expire is done, several clients can offer
- X** you the same article, and you'll accept all the copies offered you.
- X** When rnews gets run after expire, it will reject the duplicates).
- X**
- X** P O L L I N G
- X**
- X** Polling presents some article and distribution security problems,
- X** because the server has no contol over what a transmission client
- X** will ask for, and it must therefore control what it tells a client
- X** in response to a query.
- X**
- X** Articles that appear in local newsgroup hierarchies, or appear in
- X** the generally distributed USENET newsgroups with local distributions
- X** have to be filtered out from the list of message-IDs that the server
- X** gives to a client in response to a NEWNEWS query, or filtered when
- X** the server fetches the articles off the disk in response to an
- X** ARTICLE command (and therefore has complete access to the required
- X** information). Otherwise, distributions will leak.
- X**
- X** The other problem with polling is that a good client should keep track
- X** of when it last successfully polled a server, so that it doesn't force
- X** the server to dump its entire history file across the network, and this
- X** involves more file locking and manipulations routines.
- X**
- X** nntpxmit only implements active send, for now.
- X**
- X** Erik E. Fair <fair@ucbarpa.berkeley.edu>, Dec 4, 1987
- X** Stan Barber <sob@bcm.tmc.edu>, Jan 1, 1989
- X*/
- X
- X#include "../common/conf.h"
- X#include "nntpxmit.h"
- X#include <stdio.h>
- X#include <errno.h>
- X#include <ctype.h>
- X#include <sys/types.h>
- X#ifdef LAI_TCP
- X#include <sys/bsdtypes.h>
- X#endif
- X#include <sys/time.h>
- X#if defined(BSD_42) || defined(BSD_43)
- X#include <sys/resource.h>
- X#else
- X#include <sys/times.h>
- Xextern time_t time();
- X#endif
- X#include <sys/file.h>
- X#include <fcntl.h>
- X#include <signal.h>
- X#ifdef USG
- X#include "sysexits.h"
- X#else
- X#include <sysexits.h>
- X#endif
- X#ifdef SYSLOG
- X#ifdef FAKESYSLOG
- X#include "../server/fakesyslog.h"
- X#else
- X#include <syslog.h>
- X#endif
- X#endif /* SYSLOG */
- X#include "../common/nntp.h"
- X#include "llist.h"
- X
- X#define MAXFNAME BUFSIZ /* maximum filename size - big enough? */
- X#define FCLOSE(fp) if (fp) (void) fclose(fp); (fp) = (FILE *)NULL
- X
- Xchar *getline();
- Xchar *getmsgid();
- Xchar *errmsg();
- Xvoid requeue();
- XSIGRET catchsig();
- Xvoid restsig();
- Xvoid logstats();
- Xvoid log();
- Xint interrupted();
- X
- X/*
- X** Globals that certain things need.
- X**
- X** Various subroutines want the program name to report errors.
- X** The queue file, queue file pointer and current article name are
- X** there to write out the state of the queue file from a signal handler
- X** (that is, the list of unsent and (possibly) failed articles) so
- X** that when next we try sending to a given remote site, we don't send
- X** stuff we've already sent.
- X*/
- Xchar *Pname; /* this program's invocation name */
- Xchar *Host; /* current remote host */
- Xchar *Qfile; /* current queue file we're operating on */
- XFILE *Qfp; /* the (FILE *) for above */
- Xchar Article[MAXFNAME]; /* current article filename */
- X
- X/*
- X** Some flags, toggled by arguments
- X*/
- X#define TOGGLE(boolean) (boolean) = !(boolean)
- Xchar Debug = FALSE;
- Xchar Report_Stats = TRUE;
- Xchar ReQueue_Fails = TRUE;
- X
- Xchar *USAGE = "USAGE: nntpxmit [-d][-s][-r][-T][-F][-D] hostname|hostname:file [...]";
- Xchar *Fmt = "%s localhost %s[%d]: %s\n";
- Xchar *E_fopen = "fopen(%s, \"%s\"): %s";
- Xchar *E_unlk = "unlink(%s): %s";
- X
- Xll_t FailedArticles; /* list of failed articles */
- X
- Xstruct {
- X u_long offered;
- X u_long accepted;
- X u_long rejected;
- X u_long failed;
- X} Stats = {0L, 0L, 0L, 0L};
- X
- Xdouble Tbegin, Tend; /* transfer timestamps */
- X
- Xextern int errno;
- Xextern int strncmp();
- Xextern char *rindex();
- Xextern char *index();
- Xextern char *mktemp();
- Xextern char *strcpy();
- Xextern char *strcat();
- X
- X#ifdef USG
- Xvoid
- Xbzero(s, l)
- Xregister caddr_t s;
- Xregister int l;
- X{
- X while(l-- > 0) *s++ = 0;
- X}
- X#endif /* USG */
- X
- Xmain(ac, av)
- Xint ac;
- Xchar *av[];
- X{
- X register int i;
- X int transport = T_IP_TCP; /* default is IP/TCP */
- X int isQfile = TRUE; /* file arg is a Queue file */
- X#if defined(BSD_42) || defined(BSD_43)
- X struct timeval tod;
- X struct timezone tz;
- X
- X (void) gettimeofday(&tod, &tz);
- X Tbegin = tod.tv_sec + (double)tod.tv_usec/1000000.;
- X#else
- X Tbegin = (double) time((time_t *)NULL);
- X#endif
- X
- X Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]);
- X
- X if (ac < 2) {
- X fprintf(stderr, "%s: %s\n", Pname, USAGE);
- X exit(EX_USAGE);
- X }
- X
- X#ifdef SYSLOG
- X /* 4.2 BSD openlog has only two args */
- X#ifdef BSD_42
- X (void) openlog(Pname, LOG_PID);
- X#else
- X (void) openlog(Pname, LOG_PID, SYSLOG);
- X#endif /* BSD_42 */
- X#endif /* SYSLOG */
- X
- X for(i = 1; i < ac; i++) {
- X if (av[i][0] == '-') {
- X switch(av[i][1]) {
- X case 'T':
- X transport = T_IP_TCP;
- X break;
- X case 'D':
- X transport = T_DECNET;
- X break;
- X case 'F':
- X transport = T_FD;
- X break;
- X case 's':
- X TOGGLE(Report_Stats);
- X break;
- X case 'd':
- X TOGGLE(Debug);
- X break;
- X case 'r':
- X TOGGLE(ReQueue_Fails);
- X break;
- X case 'a':
- X isQfile = FALSE;
- X break;
- X default:
- X fprintf(stderr, "%s: no such option: -%c\n",
- X Pname, av[i][1]);
- X fprintf(stderr, "%s: %s\n", Pname, USAGE);
- X exit(EX_USAGE);
- X }
- X continue;
- X }
- X
- X /*
- X ** OK, it wasn't an option, therefore it must be a
- X ** hostname, filename pair.
- X **
- X ** If the user typed host::file, then it's DECNET,
- X ** whether they remembered the "-D" option or not.
- X */
- X Host = av[i];
- X if ((Qfile = index(Host, ':')) != (char *)NULL) {
- X if (Qfile[1] == ':') {
- X transport = T_DECNET;
- X *Qfile++ = '\0';
- X } else if (transport != T_FD)
- X transport = T_IP_TCP;
- X *Qfile++ = '\0';
- X } else
- X Qfile = Host;
- X
- X bzero((caddr_t)&Stats, sizeof(Stats));
- X if (isQfile) {
- X if (sendnews(Host, transport, Qfile, isQfile) && Report_Stats) {
- X logstats();
- X }
- X } else {
- X /* one-shot */
- X (void) strcpy(Article, Qfile);
- X exit(sendnews(Host, transport, Qfile, isQfile) ? EX_OK : EX_TEMPFAIL);
- X }
- X }
- X exit(EX_OK);
- X}
- X
- X/*
- X** Calculate how much time we've used,
- X** and report that (and the transfer statistics).
- X**
- X*/
- Xvoid
- Xlogstats()
- X{
- X static double ouser = 0.0, osys = 0.0;
- X double user, sys;
- X char buf[BUFSIZ];
- X#if defined(BSD_42) || defined(BSD_43)
- X struct rusage self, kids;
- X struct timeval tod;
- X struct timezone tzdummy;
- X
- X (void) getrusage(RUSAGE_SELF, &self);
- X (void) getrusage(RUSAGE_CHILDREN, &kids);
- X (void) gettimeofday(&tod, &tzdummy);
- X
- X Tend = tod.tv_sec + (double)tod.tv_usec/1000000.;
- X
- X user = self.ru_utime.tv_sec + kids.ru_utime.tv_sec +
- X (double) self.ru_utime.tv_usec/1000000. +
- X (double) kids.ru_utime.tv_usec/1000000.;
- X
- X sys = self.ru_stime.tv_sec + kids.ru_stime.tv_sec +
- X (double) self.ru_stime.tv_usec/1000000. +
- X (double) kids.ru_stime.tv_usec/1000000.;
- X#else
- X#define HZ 60.0 /* typical system clock ticks - param.h */
- X struct tms cpu;
- X
- X (void) times(&cpu);
- X
- X Tend = (double) time((time_t *)NULL);
- X user = (double)(cpu.tms_utime + cpu.tms_cutime) / HZ;
- X sys = (double)(cpu.tms_stime + cpu.tms_cstime) / HZ;
- X#endif
- X sprintf(buf,
- X "%s stats %lu offered %lu accepted %lu rejected %lu failed",
- X Host, Stats.offered, Stats.accepted, Stats.rejected,
- X Stats.failed);
- X log(L_INFO, buf);
- X sprintf(buf, "%s xmit user %.3f system %.3f elapsed %.3f",
- X Host, (user - ouser), (sys - osys), (Tend - Tbegin));
- X log(L_INFO, buf);
- X /* reset reference point */
- X Tbegin = Tend;
- X ouser = user;
- X osys = sys;
- X}
- X
- X/*
- X** Given a hostname to connect to, and a file of filenames (which contain
- X** netnews articles), send those articles to the named host using NNTP.
- X**
- X** Return code behavior is different depending upon isQfile.
- X**
- X** TRUE - return TRUE if we contacted the remote and started
- X** transferring news - this is to decide whether to
- X** record CPU and transfer statistics.
- X**
- X** FALSE - a one-shot file transfer - return TRUE or FALSE depending
- X** upon whether we successfully transferred the one article.
- X*/
- Xsendnews(host, transport, file, isQfile)
- Xchar *host, *file;
- Xint transport, isQfile;
- X{
- X#ifdef FTRUNCATE
- X char *mode = "r+"; /* so we can use ftruncate() */
- X#else
- X char *mode = "r";
- X#endif /* FTRUNCATE */
- X char *msgid;
- X
- X if ((Qfp = fopen(file, mode)) == (FILE *)NULL) {
- X char buf[BUFSIZ];
- X
- X sprintf(buf, E_fopen, file, mode, errmsg(errno));
- X log(L_WARNING, buf);
- X return(FALSE);
- X }
- X
- X /*
- X ** interlock with other copies of this process.
- X ** non-blocking.
- X */
- X if (isQfile) {
- X if (!lockfd(fileno(Qfp), file, DONT_BLOCK)) {
- X FCLOSE(Qfp);
- X return(FALSE);
- X }
- X }
- X
- X /*
- X ** Open a connection to the remote server
- X */
- X if (hello(host, transport) == FAIL) {
- X FCLOSE(Qfp);
- X return(FALSE);
- X }
- X
- X if (isQfile) {
- X /*
- X ** We're sending a batch queue:
- X ** open article
- X ** get message-ID
- X ** send "IHAVE <message-ID>" to remote
- X ** read their reply
- X ** send article if appropriate
- X ** iterate to end of queue file
- X */
- X catchsig(interrupted);
- X
- X while ((msgid = getline(Qfp, Article, sizeof(Article))) != NULL) {
- X if (!sendarticle(host, Article, msgid)) {
- X requeue(Article, msgid);
- X Article[0] = '\0';
- X cleanup();
- X goodbye(DONT_WAIT);
- X restsig();
- X return(TRUE);
- X }
- X }
- X
- X cleanup();
- X goodbye(WAIT);
- X restsig();
- X return(TRUE);
- X } else {
- X /*
- X ** Qfp is a netnews article - this is a one-shot
- X ** operation, exit code dependent upon remote's
- X ** acceptance of the article
- X */
- X register int retcode;
- X
- X FCLOSE(Qfp);
- X retcode = sendarticle(host, file, (char *) NULL);
- X goodbye(retcode ? WAIT : DONT_WAIT);
- X return(retcode && Stats.accepted == 1 && Stats.failed == 0);
- X }
- X}
- X
- X/*
- X** Perform one transfer operation:
- X** Give IHAVE command
- X** Wait for reply, and send article if they ask for it
- X** Wait for transfer confirmation, and requeue the article
- X** if they drop it.
- X** Watch all network I/O for errors, return FALSE if
- X** the connection fails and we have to cleanup.
- X*/
- Xsendarticle(host, file, msgid)
- Xchar *host;
- Xchar *file;
- Xchar *msgid;
- X{
- X register int code;
- X FILE *fp = NULL;
- X int error;
- X char buf[BUFSIZ];
- X char *e_xfer = "%s xfer: %s";
- X
- X errno = 0;
- X if (msgid == NULL || *msgid == '\0') {
- X if ((msgid = getmsgid(file, &fp)) == NULL) {
- X if (fp) { (void) fclose(fp); fp = NULL; }
- X return TRUE;
- X }
- X }
- X switch(code = ihave(msgid)) {
- X case CONT_XFER:
- X /*
- X ** They want it. Give it to 'em.
- X */
- X if (!fp) { fp = fopen(file, "r"); }
- X if (fp == NULL && errno != ENOENT) {
- X /* Worse than "No such file or directory"? */
- X sprintf(buf, E_fopen, file, "r", errmsg(errno));
- X log(L_WARNING, buf);
- X goodbye(DONT_WAIT);
- X exit(EX_OSERR);
- X }
- X if (fp == NULL) {
- X /* Hmph. The file didn't exist. */
- X error = sendcmd(".");
- X } else {
- X error = !sendfile(fp);
- X (void) fclose(fp);
- X fp = NULL;
- X }
- X if (error) {
- X sprintf(buf, "%s xfer: sendfile: %s",
- X host, errmsg(errno));
- X log(L_NOTICE, buf);
- X Stats.failed++;
- X if (fp) { (void) fclose(fp); fp = NULL; }
- X return(FALSE);
- X }
- X /*
- X ** Did the article transfer OK?
- X ** Stay tuned to this same socket to find out!
- X */
- X errno = 0;
- X if ((code = readreply(buf, sizeof(buf))) != OK_XFERED) {
- X Stats.failed++;
- X if (code < 0) {
- X if (errno > 0) {
- X sprintf(buf, e_xfer, host, errmsg(errno));
- X log(L_NOTICE, buf);
- X } else {
- X char errbuf[BUFSIZ];
- X
- X sprintf(errbuf, e_xfer, host, buf);
- X log(L_NOTICE, errbuf);
- X if (fp) { (void) fclose(fp); fp = NULL; }
- X }
- X return(FALSE);
- X }
- X if (ReQueue_Fails && code != ERR_XFERRJCT && fp != NULL) {
- X requeue(Article, msgid);
- X Article[0] = '\0';
- X }
- X }
- X break;
- X case ERR_GOTIT:
- X /* they don't want it */
- X break;
- X case ERR_XFERFAIL:
- X if (fp) { (void) fclose(fp); fp = NULL; }
- X /* they can't do it right now, but maybe later */
- X return(FALSE);
- X break;
- X default:
- X if (code < 0) {
- X if (errno > 0) {
- X sprintf(buf, e_xfer, host, errmsg(errno));
- X log(L_NOTICE, buf);
- X } else {
- X sprintf(buf, e_xfer, host, "ihave");
- X log(L_NOTICE, buf);
- X }
- X } else {
- X sprintf(buf, "%s improper response to IHAVE: %d while offering %s", host, code, Article);
- X log(L_WARNING, buf);
- X if (fp) { (void) fclose(fp); fp = NULL; }
- X }
- X return(FALSE);
- X }
- X if (fp) { (void) fclose(fp); fp = NULL; }
- X return(TRUE);
- X}
- X
- Xchar *
- Xerrmsg(code)
- Xint code;
- X{
- X extern int sys_nerr;
- X extern char *sys_errlist[];
- X static char ebuf[6+5+1];
- X
- X if (code > sys_nerr || code < 0) {
- X (void) sprintf(ebuf, "Error %d", code);
- X return ebuf;
- X } else
- X return sys_errlist[code];
- X}
- X
- X/*
- X** strip leading and trailing spaces
- X*/
- Xchar *
- Xsp_strip(s)
- Xregister char *s;
- X{
- X register char *cp;
- X
- X if (s == NULL)
- X return(NULL);
- X
- X if (*s == '\0')
- X return(s);
- X
- X cp = &s[strlen(s) - 1];
- X while(cp > s && isspace(*cp))
- X cp--;
- X
- X *++cp = '\0'; /* zap trailing spaces */
- X
- X for(cp = s; *cp && isspace(*cp); cp++)
- X continue;
- X
- X return(cp); /* return pointer to first non-space */
- X}
- X
- X/*
- X** convert `s' to lower case
- X*/
- Xchar *
- Xlcase(s)
- Xregister char *s;
- X{
- X register char *cp;
- X
- X if (s == (char *)NULL)
- X return(s);
- X
- X for(cp = s; *cp != '\0'; cp++)
- X if (isupper(*cp))
- X *cp = tolower(*cp);
- X return(s);
- X}
- X
- X/*
- X** Get the message-id header field data with a minimum of fuss.
- X*/
- Xchar *
- Xgetmsgid(file, fpp)
- Xchar *file;
- XFILE **fpp;
- X{
- X static char buf[BUFSIZ];
- X static char msgid[] = "message-id";
- X register char *cp, *cp2;
- X
- X *fpp = fopen(file, "r");
- X if (*fpp == NULL) return NULL;
- X
- X while(fgets(buf, sizeof(buf), *fpp) != NULL) {
- X switch(buf[0]) {
- X case '\n':
- X (void) fclose(*fpp);
- X *fpp = NULL;
- X return NULL; /* EOH, we failed */
- X case 'M':
- X case 'm':
- X cp = index(buf, ':');
- X if (cp == NULL) continue;
- X *cp++ = '\0';
- X if (strncmp(lcase(buf), msgid, strlen(msgid)) == 0) {
- X /* dump extraneous trash - umass.bitnet */
- X /* hope nobody quotes an '>' in a msgid */
- X cp2 = index(cp, '>');
- X if (cp2 != NULL) *++cp2 = '\0';
- X (void) rewind(*fpp);
- X return(sp_strip(cp));
- X }
- X break;
- X }
- X }
- X (void) fclose(*fpp);
- X *fpp = NULL;
- X return NULL; /* EOF, failed. */
- X}
- X
- X#ifdef notdef /* nobody obeys the triply damned protocol anyway! */
- X/*
- X** Special characters, see RFC822, appendix D.
- X*/
- Xisspecial(c)
- Xchar c;
- X{
- X char *specials = "()<>@,;:\\\".[]";
- X
- X return(index(specials, c) != (char *)NULL ? TRUE : FALSE);
- X}
- X
- X/*
- X** Check on the validity of an RFC822 message-id
- X**
- X** By The Book, RFC822 Appendix D.
- X** msg-id = "<" addr-spec ">"
- X** addr-spec = local-part "@" domain
- X** local-part = word *("." word)
- X** word = atom / quoted-string
- X** domain = sub-domain *("." sub-domain)
- X** sub-domain = domain-ref / domain-literal
- X** domain-ref = atom
- X** domain-literal = "[" *(dtext / quoted-pair) "]"
- X**
- X** NOTE: close reading of the RFC822 spec indicates that a fully
- X** qualified domain name (i.e. one with at least one dot) is
- X** NOT required in the domain part of the addr-spec. However,
- X** I've decided to be an asshole and require them, since we'll
- X** all die a slow death later on if I don't at this juncture.
- X** To disable, if you disagree with me, see the last return
- X** statement. - Erik E. Fair <fair@ucbarpa.berkeley.edu>
- X** May 30, 1986
- X*/
- Xmsgid_ok(id)
- Xregister char *id;
- X{
- X register Langle = FALSE;
- X register Rangle = FALSE;
- X register local_part = FALSE;
- X register at = FALSE;
- X register dot = FALSE;
- X
- X /* skip up to the opening angle bracket */
- X if (id == (char *)NULL || (id = index(id, '<')) == (char *)NULL)
- X return(FALSE); /* don't waste my time! */
- X
- X for(; *id != '\0'; id++) {
- X switch(*id) {
- X case '<':
- X if (Langle) return(FALSE);
- X Langle = local_part = TRUE;
- X break;
- X case '>':
- X if (Rangle || !Langle || !at) return(FALSE);
- X else Rangle = TRUE;
- X break;
- X case '@': /* should be a domain spec */
- X at = TRUE;
- X local_part = FALSE;
- X break;
- X case '.':
- X dot = at;
- X break;
- X case '\\':
- X /*
- X ** quoted pair; this disallows NULs, but how
- X ** many mailers would die if someone used one?
- X */
- X if (!local_part || (*++id) == '\0') return(FALSE);
- X break;
- X case '"':
- X /*
- X ** quoted string
- X */
- X if (!local_part) return(FALSE);
- X do {
- X switch(*++id) {
- X case '\\':
- X if ((*++id) == '\0') return(FALSE);
- X break;
- X case '\r':
- X return(FALSE);
- X }
- X } while(*id != '\0' && *id != '"');
- X break;
- X case '[':
- X /*
- X ** domain literal
- X */
- X if (local_part) return(FALSE);
- X do {
- X switch(*++id) {
- X case '\\':
- X if ((*++id) == '\0') return(FALSE);
- X break;
- X case '\r':
- X return(FALSE);
- X }
- X } while(*id != '\0' && *id != ']');
- X break;
- X default:
- X if (!isascii(*id) || iscntrl(*id) || isspace(*id) || isspecial(*id))
- X return(FALSE); /* quit immediately */
- X break;
- X }
- X }
- X return(at && dot && Langle && Rangle);
- X}
- X#else notdef
- X
- X/*
- X** Simpleton's check for message ID syntax.
- X** A concession to the realities of the ARPA Internet.
- X*/
- Xmsgid_ok(s)
- Xregister char *s;
- X{
- X register char c;
- X register in_msgid = FALSE;
- X
- X if (s == (char *)NULL)
- X return(FALSE);
- X
- X while((c = *s++) != '\0') {
- X if (!isascii(c) || iscntrl(c) || isspace(c))
- X return(FALSE);
- X switch(c) {
- X case '<':
- X in_msgid = TRUE;
- X break;
- X case '>':
- X return(in_msgid);
- X }
- X }
- X return(FALSE);
- X}
- X#endif /* notdef */
- X
- X/*
- X** Read the header of a netnews article, snatch the message-id therefrom,
- X** and ask the remote if they have that one already.
- X*/
- Xihave(id)
- Xchar *id;
- X{
- X register int code;
- X char buf[BUFSIZ];
- X
- X if (id == NULL || *id == '\0') {
- X /*
- X ** something botched locally with the article
- X ** so we don't send it, but we don't break off
- X ** communications with the remote either.
- X */
- X sprintf(buf, "%s: message-id missing!", Article);
- X log(L_DEBUG, buf);
- X return(ERR_GOTIT);
- X }
- X
- X if (!msgid_ok(id)) {
- X sprintf(buf, "%s: message-id syntax error: %s", Article, id);
- X log(L_DEBUG, buf);
- X return(ERR_GOTIT);
- X }
- X
- Xagain:
- X sprintf(buf, "IHAVE %s", id);
- X Stats.offered++;
- X
- X switch(code = converse(buf, sizeof(buf))) {
- X case CONT_XFER:
- X Stats.accepted++;
- X return(code);
- X case ERR_GOTIT:
- X Stats.rejected++;
- X return(code);
- X#ifdef AUTH
- X case ERR_NOAUTH:
- X xmitauth(Host);
- X goto again;
- X#endif
- X default:
- X return(code);
- X }
- X}
- X
- X/*
- X** Read the next line from fp into line,
- X** break it apart into filename and message-id,
- X** and return a pointer to the message-id.
- X** Returns "" if no message-id.
- X** Returns NULL at end of file.
- X*/
- Xchar *
- Xgetline(fp, line, len)
- XFILE *fp;
- Xchar *line;
- Xint len;
- X{
- X register char *cp;
- X
- X do {
- X if (fgets(line, len, fp) == NULL) return NULL;
- X line[len - 1] = '\0';
- X
- X cp = index(line, '\n');
- X if (cp != NULL) *cp = '\0';
- X } while (line[0] == '\0');
- X
- X cp = &line[0];
- X while (*cp != '\0' && !isspace(*cp)) ++cp;
- X if (*cp != '\0') {
- X *cp++ = '\0';
- X while (*cp != '\0' && isspace(*cp)) ++cp;
- X /* cp now points to the message-id, if any. */
- X }
- X return cp;
- X}
- X
- X/*
- X** OK, clean up any mess and requeue failed articles
- X*/
- Xcleanup()
- X{
- X dprintf(stderr, "%s: cleanup()\n", Pname);
- X if (Qfp == (FILE *)NULL || Qfile == (char *)NULL)
- X return;
- X
- X if ((ReQueue_Fails && Stats.failed > 0) || !feof(Qfp)) {
- X rewrite();
- X } else {
- X /*
- X ** Nothing to clean up after, reset stuff and
- X ** nuke the queue file.
- X */
- X requeue((char *)NULL, (char *)NULL);
- X if (feof(Qfp)) {
- X dprintf(stderr, "%s: unlink(%s)\n", Pname, Qfile);
- X if (unlink(Qfile) < 0) {
- X char buf[BUFSIZ];
- X
- X sprintf(buf, E_unlk, Qfile, errmsg(errno));
- X log(L_WARNING, buf);
- X }
- X }
- X FCLOSE(Qfp);
- X }
- X}
- X
- X/*
- X** Add an article file name to an allocated linked list,
- X** so that we can rewrite it back to the queue file later.
- X** Calling this with a NULL pointer resets the internal pointer.
- X*/
- Xvoid
- Xrequeue(article, msgid)
- Xchar *msgid;
- Xchar *article;
- X{
- X char buf[BUFSIZ];
- X static ll_t *lp = &FailedArticles;
- X
- X if (article == (char *)NULL) {
- X dprintf(stderr, "%s: requeue(): reset\n", Pname);
- X goto reset; /* this is for our static pointer */
- X }
- X
- X if (*article == '\0')
- X return;
- X
- X (void) strcpy(buf, article);
- X if (msgid != NULL && *msgid != '\0') {
- X (void) strcat(strcat(buf, " "), msgid);
- X }
- X
- X dprintf(stderr, "%s: requeue(%s)\n", Pname, buf);
- X if ((lp = l_alloc(lp, buf, strlen(buf) + 1)) == (ll_t *)NULL) {
- X fprintf(stderr, "%s: requeue(%s) failed, dumping fail list\n",
- X Pname, buf);
- X /*
- X ** Wow! Did you know that this could blow the stack
- X ** if we recurse too deeply? I sure didn't!
- X */
- Xreset:
- X l_free(&FailedArticles);
- X lp = &FailedArticles;
- X }
- X}
- X
- X/*
- X** Note that if I'm not running as "news" or "usenet" (or whatever
- X** account is supposed to own netnews), the resultant file will be the
- X** wrong ownership, permissions, etc.
- X*/
- Xrewrite()
- X{
- X register ll_t *lp;
- X register FILE *tmpfp;
- X register int nart = 0;
- X char *mode = "w+";
- X static char template[] = "/tmp/nntpxmitXXXXXX";
- X char buf[BUFSIZ];
- X static char *tempfile = (char *)NULL;
- X
- X dprintf(stderr, "%s: rewrite(%s)\n", Pname, Qfile);
- X
- X if (tempfile == (char *)NULL) /* should only need this once */
- X tempfile = mktemp(template);
- X
- X if ((tmpfp = fopen(tempfile, mode)) == (FILE *)NULL) {
- X sprintf(buf, E_fopen, tempfile, mode, errmsg(errno));
- X log(L_WARNING, buf);
- X FCLOSE(Qfp);
- X return;
- X }
- X
- X /*
- X ** Requeue the rest of the queue file first,
- X ** so that failed articles (if any) go to the end
- X ** of the new file.
- X */
- X if (!feof(Qfp)) {
- X dprintf(stderr, "%s: copying the unused portion of %s to %s\n",
- X Pname, Qfile, tempfile);
- X while(fgets(buf, sizeof(buf), Qfp) != (char *)NULL)
- X (void) fputs(buf, tmpfp);
- X }
- X
- X /*
- X ** Here we write out the filenames of articles which
- X ** failed at the remote end.
- X */
- X dprintf(stderr, "%s: writing failed article filenames to %s\n",
- X Pname, tempfile);
- X L_LOOP(lp, FailedArticles) {
- X fprintf(tmpfp, "%s\n", lp->l_item);
- X nart++;
- X }
- X dprintf(stderr, "%s: wrote %d article filenames to %s\n",
- X Pname, nart, tempfile);
- X
- X (void) fflush(tmpfp);
- X /*
- X ** If writing the temp file failed (maybe /tmp is full?)
- X ** back out and leave the queue file exactly as it is.
- X */
- X if (ferror(tmpfp)) {
- X sprintf(buf, "rewrite(): copy to %s failed", tempfile);
- X log(L_WARNING, buf);
- X (void) fclose(tmpfp);
- X FCLOSE(Qfp);
- X if (unlink(tempfile) < 0) {
- X sprintf(buf, E_unlk, tempfile, errmsg(errno));
- X log(L_WARNING, buf);
- X }
- X requeue((char *)NULL,(char *)NULL); /* reset */
- X return;
- X }
- X
- X rewind(tmpfp);
- X#ifdef FTRUNCATE
- X rewind(Qfp);
- X if (ftruncate(fileno(Qfp), (off_t)0) < 0) {
- X sprintf(buf, "ftruncate(%s, 0): %s", Qfile, errmsg(errno));
- X log(L_WARNING, buf);
- X FCLOSE(Qfp);
- X (void) fclose(tmpfp);
- X if (unlink(tempfile) < 0) {
- X sprintf(buf, E_unlk, tempfile, errmsg(errno));
- X log(L_WARNING, buf);
- X }
- X requeue((char *)NULL,(char *)NULL); /* reset */
- X return;
- X }
- X#else
- X FCLOSE(Qfp); /* we just nuked our lock here (lockfd) */
- X if ((Qfp = fopen(Qfile, mode)) == (FILE *)NULL) {
- X sprintf(buf, E_fopen, Qfile, mode, errmsg(errno));
- X log(L_WARNING, buf);
- X (void) fclose(tmpfp);
- X if (unlink(tempfile) < 0) {
- X sprintf(buf, E_unlk, tempfile, errmsg(errno));
- X log(L_WARNING, buf);
- X }
- X requeue((char *)NULL,(char *)NULL); /* reset */
- X return;
- X }
- X /* Try to get our lock back (but continue whether we do or not) */
- X (void) lockfd(fileno(Qfp), Qfile, DONT_BLOCK);
- X#endif /* FTRUNCATE */
- X
- X dprintf(stderr, "%s: copying %s back to %s\n", Pname, tempfile, Qfile);
- X while(fgets(buf, sizeof(buf), tmpfp) != (char *)NULL)
- X (void) fputs(buf, Qfp);
- X
- X (void) fflush(Qfp);
- X if (ferror(Qfp)) {
- X sprintf(buf, "rewrite(): copy to %s failed", Qfile);
- X log(L_WARNING, buf);
- X }
- X (void) fclose(tmpfp);
- X FCLOSE(Qfp);
- X if (unlink(tempfile) < 0) {
- X sprintf(buf, E_unlk, tempfile, errmsg(errno));
- X log(L_WARNING, buf);
- X }
- X requeue((char *)NULL,(char *)NULL); /* reset */
- X dprintf(stderr, "%s: rewrite(%s): done\n", Pname, Qfile);
- X return;
- X}
- X
- X/*
- X** Signal stuff
- X**
- X** There's probably too much stuff to do in this signal
- X** handler, but we're going to exit anyway...
- X*/
- Xinterrupted(sig)
- Xint sig;
- X{
- X char buf[BUFSIZ];
- X
- X#ifndef RELSIG
- X catchsig(SIG_IGN); /* for System V - hope we're quick enough */
- X#endif /* RELSIG */
- X sprintf(buf, "%s signal %d", Host, sig);
- X log(L_NOTICE, buf);
- X requeue(Article,(char *)NULL);
- X cleanup();
- X if (Report_Stats)
- X logstats();
- X goodbye(DONT_WAIT);
- X exit(EX_TEMPFAIL);
- X}
- X
- Xstruct {
- X int signo;
- X ifunp state;
- X} SigList[] = {
- X {SIGHUP},
- X {SIGINT},
- X {SIGQUIT},
- X {SIGTERM},
- X {NULL}
- X};
- X
- XSIGRET
- Xcatchsig(handler)
- Xifunp handler;
- X{
- X register int i;
- X
- X if (handler != SIG_IGN) {
- X for(i = 0; SigList[i].signo != NULL; i++) {
- X SigList[i].state = signal(SigList[i].signo, handler);
- X }
- X } else {
- X for(i = 0; SigList[i].signo != NULL; i++) {
- X (void) signal(SigList[i].signo, handler);
- X }
- X }
- X}
- X
- Xvoid
- Xrestsig()
- X{
- X register int i;
- X
- X for(i = 0; SigList[i].signo != NULL; i++) {
- X if (SigList[i].state != (ifunp)(-1))
- X (void) signal(SigList[i].signo, SigList[i].state);
- X }
- X}
- X
- X/*
- X** log stuff
- X*/
- Xvoid
- Xlog(importance, error)
- Xint importance;
- Xchar *error;
- X{
- X int skip = FALSE;
- X FILE *report = (importance == L_INFO ? stdout : stderr);
- X fprintf(report, "%s: %s\n", Pname, error);
- X#ifdef SYSLOG
- X switch(importance) {
- X#ifdef LOG
- X case L_DEBUG: importance = LOG_DEBUG; break;
- X#else
- X case L_DEBUG: skip = TRUE; break;
- X#endif
- X case L_INFO: importance = LOG_INFO; break;
- X case L_NOTICE: importance = LOG_NOTICE; break;
- X case L_WARNING: importance = LOG_WARNING; break;
- X default: importance = LOG_DEBUG; break;
- X }
- X if (skip == FALSE) syslog(importance, error);
- X#endif /* SYSLOG */
- X}
- X
- X/*
- X** Lock a file descriptor
- X**
- X** NOTE: if the appropriate system calls are unavailable,
- X** this subroutine is a no-op.
- X*/
- Xlockfd(fd, file, non_blocking)
- Xint fd, non_blocking;
- Xchar *file; /* just for error reporting */
- X{
- X char buf[BUFSIZ];
- X#ifdef USG
- X#ifdef F_TLOCK
- X if (lockf(fd, (non_blocking ? F_TLOCK : F_LOCK), 0) < 0) {
- X if (errno != EACCES) {
- X sprintf(buf, "lockf(%s): %s\n", file, errmsg(errno));
- X log(L_WARNING, buf);
- X }
- X return(FALSE);
- X }
- X#endif /* F_TLOCK */
- X#else
- X#ifdef LOCK_EX
- X if (flock(fd, LOCK_EX|(non_blocking ? LOCK_NB : 0)) < 0) {
- X if (errno != EWOULDBLOCK) {
- X sprintf(buf, "flock(%s): %s\n", file, errmsg(errno));
- X log(L_WARNING, buf);
- X }
- X return(FALSE);
- X }
- X#endif /* LOCK_EX */
- X#endif /* USG */
- X return(TRUE);
- X}
- SHAR_EOF
- if test 27564 -ne "`wc -c < 'nntpxmit.c'`"
- then
- echo shar: error transmitting "'nntpxmit.c'" '(should have been 27564 characters)'
- fi
- echo shar: extracting "'remote.c'" '(10680 characters)'
- sed 's/^ X//' << \SHAR_EOF > 'remote.c'
- X#ifndef lint
- Xstatic char * rcsid = "@(#)$Header: remote.c,v 1.6 91/03/19 03:03:28 sob Exp $";
- X#endif
- X/*
- X** remote communication routines for NNTP/SMTP style communication.
- X**
- X************
- X** This version has been modified to support mmap()'ing of article files
- X** on systems that support it.
- X**
- X** David Robinson (david@elroy.jpl.nasa.gov) and
- X** Steve Groom (stevo@elroy.jpl.nasa.gov), June 30, 1989.
- X**
- X************
- X**
- X** sendcmd - return TRUE on error.
- X**
- X** readreply - return reply code or FAIL for error;
- X** modifies buffer passed to it.
- X**
- X** converse - sendcmd() & readreply();
- X** return reply code or FAIL for error;
- X** modifies buffer passed to it.
- X**
- X** hello - establish connection with remote;
- X** check greeting code.
- X**
- X** goodbye - give QUIT command, and shut down connection.
- X**
- X** sfgets - safe fgets(); does fgets with TIMEOUT.
- X** (N.B.: possibly unportable stdio macro ref in here)
- X**
- X** rfgets - remote fgets() (calls sfgets());
- X** does SMTP dot escaping and
- X** \r\n -> \n conversion.
- X**
- X** sendfile - send a file with SMTP dot escaping and
- X** \n -> \r\n conversion.
- X**
- X** Erik E. Fair <fair@ucbarpa.berkeley.edu>
- X*/
- X#include "../common/conf.h"
- X#include "nntpxmit.h"
- X#include <sys/types.h>
- X#include <sys/socket.h>
- X#include <errno.h>
- X#ifdef LAI_TCP
- X#include <net/errno.h>
- X#endif
- X#include <stdio.h>
- X#include <ctype.h>
- X#include <setjmp.h>
- X#include <signal.h>
- X#ifdef dgux
- X#define _IOERR _IO_ERR
- X#endif
- X#ifdef SYSLOG
- X#ifdef FAKESYSLOG
- X#include "../server/fakesyslog.h"
- X#else
- X#include <syslog.h>
- X#endif
- X#endif
- X#include "get_tcp_conn.h"
- X#include "../common/nntp.h"
- X
- Xstatic jmp_buf SFGstack;
- XFILE *rmt_rd;
- XFILE *rmt_wr;
- Xchar *sfgets();
- Xchar *rfgets();
- X
- Xextern int errno;
- Xextern char Debug;
- Xextern char *errmsg();
- Xextern char *strcpy();
- Xextern void log();
- X
- X/*
- X** send cmd to remote, terminated with a CRLF.
- X*/
- Xsendcmd(cmd)
- Xchar *cmd;
- X{
- X if (cmd == (char *)NULL)
- X return(TRUE); /* error */
- X dprintf(stderr, ">>> %s\n", cmd); /* DEBUG */
- X (void) fprintf(rmt_wr, "%s\r\n", cmd);
- X (void) fflush(rmt_wr);
- X return(ferror(rmt_wr));
- X}
- X
- X/*
- X** read a reply line from the remote server and return the code number
- X** as an integer, and the message in a buffer supplied by the caller.
- X** Returns FAIL if something went wrong.
- X*/
- Xreadreply(buf, size)
- Xregister char *buf;
- Xint size;
- X{
- X register char *cp;
- X register int len;
- X
- X if (buf == (char *)NULL || size <= 0)
- X return(FAIL);
- X
- X /*
- X ** make sure it's invalid, unless we say otherwise
- X */
- X buf[0] = '\0';
- X
- X /*
- X ** read one line from the remote
- X */
- X if (sfgets(buf, size, rmt_rd) == NULL)
- X return(FAIL); /* error reading from remote */
- X
- X /*
- X ** Make sure that what the remote sent us had a CRLF at the end
- X ** of the line, and then null it out.
- X */
- X if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r' &&
- X *(cp + 1) == '\n')
- X {
- X *cp = '\0';
- X } else
- X return(FAIL); /* error reading from remote */
- X
- X dprintf(stderr, "%s\n", buf); /* DEBUG */
- X /*
- X ** Skip any non-digits leading the response code
- X ** and then convert the code from ascii to integer for
- X ** return from this routine.
- X */
- X cp = buf;
- X while(*cp != '\0' && isascii(*cp) && !isdigit(*cp))
- X cp++; /* skip anything leading */
- X
- X if (*cp == '\0' || !isascii(*cp))
- X return(FAIL); /* error reading from remote */
- X
- X return(atoi(cp));
- X}
- X
- X/*
- X** send a command to the remote, and wait for a response
- X** returns the response code, and the message in the buffer
- X*/
- Xconverse(buf, size)
- Xchar *buf;
- Xint size;
- X{
- X register int resp;
- X
- X if (sendcmd(buf))
- X return(FAIL); /* Ooops! Something went wrong in xmit */
- X /*
- X ** Skip the silly 100 series messages, since they're not the
- X ** final response we can expect
- X */
- X while((resp = readreply(buf, size)) >= 100 && resp < 200)
- X continue;
- X return(resp);
- X}
- X
- X/*
- X** Contact the remote server and set up the two global FILE pointers
- X** to that descriptor.
- X**
- X** I can see the day when this routine will have 8 args: one for
- X** hostname, and one for each of the seven ISO Reference Model layers
- X** for networking. A curse upon those involved with the ISO protocol
- X** effort: may they be forced to use the network that they will create,
- X** as opposed to something that works (like the Internet).
- X*/
- Xhello(host, transport)
- Xchar *host;
- Xint transport;
- X{ char *service;
- X char *rmode = "r";
- X char *wmode = "w";
- X char *e_fdopen = "fdopen(%d, \"%s\"): %s";
- X int socket0, socket1; /* to me (bad pun) */
- X char buf[BUFSIZ];
- X
- X switch(transport) {
- X case T_IP_TCP:
- X service = "nntp";
- X socket0 = get_tcp_conn(host, service);
- X break;
- X case T_DECNET:
- X#ifdef DECNET
- X (void) signal(SIGPIPE, SIG_IGN);
- X service = "NNTP";
- X socket0 = dnet_conn(host, service, 0, 0, 0, 0, 0);
- X if (socket0 < 0) {
- X switch(errno) {
- X case EADDRNOTAVAIL:
- X socket0 = NOHOST;
- X break;
- X case ESRCH:
- X socket0 = NOSERVICE;
- X break;
- X }
- X }
- X break;
- X#else
- X log(L_WARNING, "no DECNET support compiled in");
- X return(FAIL);
- X#endif
- X case T_FD:
- X service = "with a smile";
- X socket0 = atoi(host);
- X break;
- X }
- X
- X if (socket0 < 0) {
- X switch(socket0) {
- X case NOHOST:
- X sprintf(buf, "%s host unknown", host);
- X log(L_WARNING, buf);
- X return(FAIL);
- X case NOSERVICE:
- X sprintf(buf, "%s service unknown: %s", host, service);
- X log(L_WARNING, buf);
- X return(FAIL);
- X case FAIL:
- X sprintf(buf, "%s hello: %s", host, errmsg(errno));
- X log(L_NOTICE, buf);
- X return(FAIL);
- X }
- X }
- X
- X if ((socket1 = dup(socket0)) < 0) {
- X sprintf(buf, "dup(%d): %s", socket0, errmsg(errno));
- X log(L_WARNING, buf);
- X (void) close(socket0);
- X return(FAIL);
- X }
- X
- X if ((rmt_rd = fdopen(socket0, rmode)) == (FILE *)NULL) {
- X sprintf(buf, e_fdopen, socket0, rmode);
- X log(L_WARNING, buf);
- X (void) close(socket0);
- X (void) close(socket1);
- X return(FAIL);
- X }
- X
- X if ((rmt_wr = fdopen(socket1, wmode)) == (FILE *)NULL) {
- X sprintf(buf, e_fdopen, socket1, wmode);
- X log(L_WARNING, buf);
- X (void) fclose(rmt_rd);
- X rmt_rd = (FILE *)NULL;
- X (void) close(socket1);
- X return(FAIL);
- X }
- X
- X switch(readreply(buf, sizeof(buf))) {
- X case OK_CANPOST:
- X case OK_NOPOST:
- X if (ferror(rmt_rd)) {
- X goodbye(DONT_WAIT);
- X return(FAIL);
- X }
- X break;
- X default:
- X if (buf[0] != '\0') {
- X char err[BUFSIZ];
- X
- X sprintf(err, "%s greeted us with %s", host, buf);
- X log(L_NOTICE, err);
- X }
- X goodbye(DONT_WAIT);
- X return(FAIL);
- X }
- X return(NULL);
- X}
- X
- X/*
- X** Say goodbye to the nice remote server.
- X**
- X** We trap SIGPIPE because the socket might already be gone.
- X*/
- Xgoodbye(wait_for_reply)
- Xint wait_for_reply;
- X{
- X register ifunp pstate = signal(SIGPIPE, SIG_IGN);
- X
- X if (sendcmd("QUIT"))
- X wait_for_reply = FALSE; /* override, something's wrong. */
- X /*
- X ** I don't care what they say to me; this is just being polite.
- X */
- X if (wait_for_reply) {
- X char buf[BUFSIZ];
- X
- X (void) readreply(buf, sizeof(buf));
- X }
- X (void) fclose(rmt_rd);
- X rmt_rd = (FILE *)NULL;
- X (void) fclose(rmt_wr);
- X rmt_wr = (FILE *)NULL;
- X if (pstate != (ifunp)(-1))
- X (void) signal(SIGPIPE, pstate);
- X}
- X
- Xstatic SIGRET
- Xto_sfgets()
- X{
- X longjmp(SFGstack, 1);
- X}
- X
- X/*
- X** `Safe' fgets, ala sendmail. This fgets will timeout after some
- X** period of time, on the assumption that if the remote did not
- X** return, they're gone.
- X** WARNING: contains a possibly unportable reference to stdio
- X** error macros.
- X*/
- Xchar *
- Xsfgets(buf, size, fp)
- Xchar *buf;
- Xint size;
- XFILE *fp;
- X{
- X register char *ret;
- X int esave;
- X
- X if (buf == (char *)NULL || size <= 0 || fp == (FILE *)NULL)
- X return((char *)NULL);
- X if (setjmp(SFGstack)) {
- X (void) alarm(0); /* reset alarm clock */
- X (void) signal(SIGALRM, SIG_DFL);
- X#ifdef apollo
- X fp->_flag |= _SIERR;
- X#else
- X fp->_flag |= _IOERR; /* set stdio error */
- X#endif
- X#ifndef ETIMEDOUT
- X errno = EPIPE; /* USG doesn't have ETIMEDOUT*/
- X#else
- X errno = ETIMEDOUT; /* connection timed out */
- X#endif
- X return((char *)NULL); /* bad read, remote time out */
- X }
- X (void) signal(SIGALRM, to_sfgets);
- X (void) alarm(TIMEOUT);
- X ret = fgets(buf, size, fp);
- X esave = errno;
- X (void) alarm(0); /* reset alarm clock */
- X (void) signal(SIGALRM, SIG_DFL); /* reset SIGALRM */
- X errno = esave;
- X return(ret);
- X}
- X
- X/*
- X** Remote fgets - converts CRLF to \n, and returns NULL on `.' EOF from
- X** the remote. Otherwise it returns its first argument, like fgets(3).
- X*/
- Xchar *
- Xrfgets(buf, size, fp)
- Xchar *buf;
- Xint size;
- XFILE *fp;
- X{
- X register char *cp = buf;
- X register int len;
- X
- X if (buf == (char *)NULL || size <= 0 || fp == (FILE *)NULL)
- X return((char *)NULL);
- X *cp = '\0';
- X if (sfgets(buf, size, fp) == (char *)NULL)
- X return((char *)NULL);
- X
- X /* <CRLF> => '\n' */
- X if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r') {
- X *cp++ = '\n';
- X *cp = '\0';
- X }
- X
- X /* ".\n" => EOF */
- X cp = buf;
- X if (*cp++ == '.' && *cp == '\n') {
- X return((char *)NULL); /* EOF */
- X }
- X
- X /* Dot escaping */
- X if (buf[0] == '.')
- X (void) strcpy(&buf[0], &buf[1]);
- X return(buf);
- X}
- X
- X/*
- X** send the contents of an open file descriptor to the remote,
- X** with appropriate RFC822 filtering (e.g. CRLF line termination,
- X** and dot escaping). Return FALSE if something went wrong.
- X*/
- Xsendfile(fp)
- XFILE *fp;
- X{
- X register int c;
- X register FILE *remote = rmt_wr;
- X register int nl = TRUE; /* assume we start on a new line */
- X
- X#ifdef MMAP
- X register char *mbufr,*mptr;
- X long msize;
- X long offset;
- X struct stat sbuf;
- X char buf[BUFSIZ];
- X#endif MMAP
- X
- X/*
- X** I'm using putc() instead of fputc();
- X** why do a subroutine call when you don't have to?
- X** Besides, this ought to give the C preprocessor a work-out.
- X*/
- X#ifdef MMAP
- X#define PUTC(c) if (putc(c, remote) == EOF) {\
- X (void) munmap (mbufr, sbuf.st_size);\
- X return(FALSE); }
- X#else !MMAP
- X#define PUTC(c) if (putc(c, remote) == EOF) return(FALSE)
- X#endif !MMAP
- X
- X if (fp == (FILE *)NULL)
- X return(FALSE);
- X
- X#ifdef MMAP
- X /* map the article into memory */
- X (void) fstat(fileno(fp), &sbuf);
- X mbufr = mmap (0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
- X if(mbufr == (char *) -1){
- X sprintf(buf, "sendfile: mmap failed: %s", errmsg(errno));
- X log(L_NOTICE, buf);
- X return(FALSE);
- X }
- X
- X mptr = mbufr; /* start of article in memory */
- X msize = sbuf.st_size; /* size of article (bytes) */
- X while(msize-- > 0) {
- X c = *mptr++;
- X#else !MMAP
- X /*
- X ** the second test makes no sense to me,
- X ** but System V apparently needed it...
- X */
- X while((c = fgetc(fp)) != EOF && !feof(fp)) {
- X#endif !MMAP
- X switch(c) {
- X case '\177': /* skip deletes... */
- X break;
- X case '\n':
- X PUTC('\r'); /* \n -> \r\n */
- X PUTC(c);
- X nl = TRUE; /* for dot escaping */
- X break;
- X case '.':
- X if (nl) {
- X PUTC(c); /* add a dot */
- X nl = FALSE;
- X }
- X PUTC(c);
- X break;
- X default:
- X PUTC(c);
- X nl = FALSE;
- X break;
- X }
- X }
- X if (!nl) {
- X PUTC('\r');
- X PUTC('\n');
- X }
- X#ifdef MMAP
- X (void) munmap (mbufr, sbuf.st_size);
- X#endif MMAP
- X return( !(sendcmd(".") || ferror(fp)) );
- X}
- SHAR_EOF
- if test 10680 -ne "`wc -c < 'remote.c'`"
- then
- echo shar: error transmitting "'remote.c'" '(should have been 10680 characters)'
- fi
- echo "Done with directory 'xmit'"
- cd ..
- fi
- if test -d 'xfer'
- then
- echo "Entering directory 'xfer'"
- cd xfer
- echo shar: extracting "'nntpxfer.c'" '(12837 characters)'
- sed 's/^ X//' << \SHAR_EOF > 'nntpxfer.c'
- X#ifndef lint
- Xstatic char * scsid = "@(#)$Header: nntpxfer.c,v 1.10 91/03/19 03:03:45 sob Exp $";
- X#endif
- X/*
- X * nntpxfer
- X *
- X * Connects to the specified nntp server, and transfers all new news
- X * since the last successful invocation.
- X *
- X * last successful invocation date and time are stored in a file at
- X * /usr/spool/news/nntp.<hostname> as
- X * groups YYMMDD HHMMSS distributions\n
- X * in case you need to edit it. You can also override this on
- X * the command line in the same format, in which case the file won't
- X * be updated.
- X *
- X * Brian Kantor, UCSD 1986
- X * (some bug fixes by ambar@athena.mit.edu)
- X * Modified to use NNTP distribution conf.h file and nntpxmit's get_tcp_conn.c
- X * subroutines so that nntpxfer could be used on more systems.
- X * Stan Barber, November 7, 1989 <sob@bcm.tmc.edu>
- X *
- X */
- X
- X#include "../common/conf.h"
- X#ifdef DEBUG
- X#undef SYSLOG
- X#endif
- X
- X#include <sys/types.h>
- X#ifdef LAI_TCP
- X#include <sys/bsdtypes.h>
- X#endif
- X#ifdef NDIR
- X#ifdef M_XENIX
- X#include <sys/ndir.h>
- X#else
- X#include <ndir.h>
- X#endif
- X#else
- X#include <sys/dir.h>
- X#endif
- X#ifdef USG
- X#include <time.h>
- X#else
- X#include <sys/time.h>
- X#endif
- X
- X#include <stdio.h>
- X#include <errno.h>
- X#include <ctype.h>
- X#include <setjmp.h>
- X#ifndef NONETDB
- X#include <netdb.h>
- X#endif
- X#include <signal.h>
- X#ifdef SYSLOG
- X#ifdef FAKESYSLOG
- X#include "../server/fakesyslog.h"
- X#else
- X#include <syslog.h>
- X#endif
- X#endif
- X
- X#ifdef DBM
- X# ifdef DBZ
- X# include <dbz.h>
- X#else /* DBZ */
- X# undef NULL
- X# include <dbm.h>
- X# undef NULL
- X# define NULL 0
- X#endif /* DBZ */
- X#endif /* DBM */
- X
- X#ifdef NDBM
- X#include <ndbm.h>
- X#include <fcntl.h>
- Xstatic DBM *db = NULL;
- X#endif
- X#ifndef TIMEOUT
- X#define TIMEOUT (30*60)
- X#endif
- X#ifndef MAX_ARTICLES
- X#define MAX_ARTICLES 4096
- X#endif
- X
- Xchar *malloc();
- Xchar *strcpy();
- Xchar *strcat();
- Xchar *rindex();
- Xlong time();
- Xu_long inet_addr();
- X
- Xextern int errno;
- Xchar *artlist[MAX_ARTICLES];
- Xint server; /* stream socket to the nntp server */
- XFILE * rd_fp, * wr_fp;
- Xint newart, dupart, misart;
- Xchar * Pname;
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar *argv[];
- X{
- X FILE *dtfile; /* where last xfer date/time stored */
- X char buf[BUFSIZ];
- X char lastdate[16];
- X char distributions[BUFSIZ];
- X char dtname[128];
- X char newsgroups[BUFSIZ];
- X char lasttime[16];
- X int i;
- X int omitupdate = 0; /* 1 = don't update datetime */
- X long clock;
- X long newdate, newtime;
- X struct tm *now;
- X Pname = ((Pname = rindex(argv[0], '/')) ? Pname + 1 : argv[0]);
- X /* OPTIONS
- X argv[1] MUST be the host name
- X argv[2-4] MAY be "newsgroups YYMMDD HHMMSS"
- X argv[5] MAY be distributions
- X (otherwise use 2-4/5 from the file
- X "/usr/spool/news/nntp.hostname")
- X */
- X
- X if (argc != 2 && argc != 5 && argc != 6)
- X {
- X (void) printf("Usage: %s host [groups YYMMDD HHMMSS [<dist>]]\n",
- X argv[0]);
- X exit(1);
- X }
- X
- X if (argc > 2)
- X {
- X omitupdate=1;
- X (void) strcpy(newsgroups, argv[2]);
- X (void) strcpy(lastdate, argv[3]);
- X (void) strcpy(lasttime, argv[4]);
- X (void) strcpy(distributions, "");
- X if (argc > 5)
- X (void) strcpy(distributions, argv[5]);
- X }
- X else
- X {
- X (void) sprintf(dtname, "%s/nntp.%s",SPOOLDIR,argv[1]);
- X dtfile = fopen(dtname, "r");
- X if (dtfile == (FILE *) 0)
- X {
- X (void) printf("%s not found; using * 860101 000000 \n",
- X dtname);
- X (void) strcpy(newsgroups, "*");
- X (void) strcpy(lastdate, "860101");
- X (void) strcpy(lasttime, "000000");
- X (void) strcpy(distributions, "");
- X }
- X else
- X {
- X if (fscanf(dtfile, "%s %s %s %s",
- X newsgroups, lastdate, lasttime, distributions) < 3)
- X {
- X (void) printf("%s invalid; using * 860101 000000\n",
- X dtname);
- X (void) strcpy(newsgroups, "*");
- X (void) strcpy(lastdate, "860101");
- X (void) strcpy(lasttime, "000000");
- X (void) strcpy(distributions, "");
- X }
- X (void) fclose(dtfile);
- X }
- X clock = time((long *)0);
- X now = gmtime(&clock);
- X newdate = (now->tm_year * 10000) +
- X ((now->tm_mon + 1) * 100) + now->tm_mday;
- X newtime = (now->tm_hour * 10000) +
- X (now->tm_min * 100) + now->tm_sec;
- X#ifdef DEBUG
- X printf("server is %s\n",argv[1]);
- X printf("lastdate is %s\n",lastdate);
- X printf("lasttime is %s\n",lasttime);
- X printf("newsgroups is '%s'\n",newsgroups);
- X printf("distributions is '%s'\n",distributions);
- X#endif
- X }
- X#ifdef SYSLOG
- X#ifdef BSD_42
- X openlog("nntpxfer", LOG_PID);
- X#else
- X openlog("nntpxfer", LOG_PID, SYSLOG);
- X#endif
- X#endif
- X
- X#ifdef DBM
- X if (dbminit(HISTORY_FILE) < 0)
- X {
- X#ifdef SYSLOG
- X syslog(LOG_ERR,"couldn't open history file: %m");
- X#else
- X perror("nntpxfer: couldn't open history file");
- X#endif
- X exit(1);
- X }
- X#endif
- X#ifdef NDBM
- X if ((db = dbm_open(HISTORY_FILE, O_RDONLY, 0)) == NULL)
- X {
- X#ifdef SYSLOG
- X syslog(LOG_ERR,"couldn't open history file: %m");
- X#else
- X perror("nntpxfer: couldn't open history file");
- X#endif
- X exit(1);
- X }
- X#endif
- X if ((server = get_tcp_conn(argv[1],"nntp")) < 0)
- X {
- X#ifdef SYSLOG
- X syslog(LOG_ERR,"could not open socket: %m");
- X#else
- X perror("nntpxfer: could not open socket");
- X#endif
- X exit(1);
- X }
- X if ((rd_fp = fdopen(server,"r")) == (FILE *) 0){
- X#ifdef SYSLOG
- X syslog(LOG_ERR,"could not fdopen socket: %m");
- X#else
- X perror("nntpxfer: could not fdopen socket");
- X#endif
- X exit(1);
- X }
- X
- X#ifdef SYSLOG
- X syslog(LOG_DEBUG,"connected to nntp server at %s", argv[1]);
- X#endif
- X#ifdef DEBUG
- X printf("connected to nntp server at %s\n", argv[1]);
- X#endif
- X /*
- X * ok, at this point we're connected to the nntp daemon
- X * at the distant host.
- X */
- X /* get the greeting herald */
- X (void) sockread(buf);
- X#ifdef DEBUG
- X (void) printf("%s\n", buf);
- X#endif
- X if (buf[0] != '2') /* uh-oh, something's wrong! */
- X {
- X#ifdef SYSLOG
- X syslog(LOG_NOTICE,"protocol error: got '%s'\n", buf);
- X#else
- X (void) printf("%s: protocol error: got '%s'\n", Pname,buf);
- X#endif
- X (void) close(server);
- X exit(1);
- X }
- X
- X
- X /* first, tell them we're a slave process to get priority */
- X sockwrite("SLAVE");
- X (void) sockread(buf);
- X#ifdef DEBUG
- X (void) printf("%s\n", buf);
- X#endif
- X if (buf[0] != '2') /* uh-oh, something's wrong! */
- X {
- X#ifdef SYSLOG
- X syslog(LOG_NOTICE,"protocol error: got '%s'", buf);
- X#else
- X (void) printf("%s: protocol error: got '%s'\n", Pname,buf);
- X#endif
- X (void) close(server);
- X exit(1);
- X }
- X
- X /* now, ask for a list of new articles */
- X if (strlen(distributions))
- X (void) sprintf(buf,"NEWNEWS %s %s %s GMT <%s>",
- X newsgroups, lastdate, lasttime, distributions);
- X else
- X (void) sprintf(buf,"NEWNEWS %s %s %s GMT",
- X newsgroups, lastdate, lasttime);
- X sockwrite(buf);
- X (void) sockread(buf);
- X#ifdef DEBUG
- X (void) printf("%s\n", buf);
- X#endif
- X if (buf[0] != '2') /* uh-oh, something's wrong! */
- X {
- X#ifdef SYSLOG
- X syslog(LOG_NOTICE,"protocol error: got '%s'", buf);
- X#else
- X (void) printf("%s: protocol error: got '%s'\n", Pname,buf);
- X#endif
- X (void) close(server);
- X exit(1);
- X }
- X /* and here comes the list, terminated with a "." */
- X#ifdef DEBUG
- X (void) printf("data\n");
- X#endif
- X dupart = newart = 0;
- X while (1)
- X {
- X (void) sockread(buf);
- X if (!strcmp(buf,"."))
- X break;
- X if (wewant(buf))
- X {
- X if (newart >= MAX_ARTICLES)
- X {
- X omitupdate=1;
- X continue;
- X }
- X artlist[newart] = malloc((unsigned)(strlen(buf)+1));
- X (void) strcpy(artlist[newart], buf);
- X newart++;
- X }
- X else
- X dupart++;
- X }
- X#ifdef DEBUG
- X (void) printf(".\n%d new, %d dup articles\n", newart, dupart);
- X#endif
- X
- X /* now that we know which articles we want, retrieve them */
- X for (i=0; i < newart; i++)
- X (void) artfetch(artlist[i]);
- X
- X#ifdef DEBUG
- X (void) printf("%d missing articles\n", misart);
- X#endif
- X /* we're all done, so tell them goodbye */
- X sockwrite("QUIT");
- X (void) sockread(buf);
- X#ifdef DEBUG
- X (void) printf("%s\n", buf);
- X#endif
- X if (buf[0] != '2') /* uh-oh, something's wrong! */
- X {
- X#ifdef SYSLOG
- X syslog(LOG_NOTICE,"error: got '%s'", buf);
- X#else
- X (void) printf("%s: error: got '%s'\n", Pname,buf);
- X#endif
- X (void) close(server);
- X exit(1);
- X }
- X (void) close(server);
- X
- X /* do we want to update the timestamp file? */
- X if (!omitupdate)
- X {
- X (void) sprintf(buf, "%s %06d %06d %s\n",
- X newsgroups, newdate, newtime, distributions);
- X#ifdef DEBUG
- X (void) printf("updating %s:\n\t%s\n", dtname, buf);
- X#endif
- X dtfile = fopen(dtname, "w");
- X if (dtfile == (FILE *) 0)
- X {
- X perror(dtname);
- X exit(1);
- X }
- X (void) fputs(buf,dtfile);
- X (void) fclose(dtfile);
- X }
- X exit(0);
- X}
- X
- Xartfetch(articleid)
- Xchar *articleid;
- X {
- X#ifdef DEBUG
- X int lines = 0;
- X#endif
- X char buf[BUFSIZ];
- X FILE *inews;
- X
- X /* now, ask for the article */
- X (void) sprintf(buf,"ARTICLE %s", articleid);
- X sockwrite(buf);
- X (void) sockread(buf);
- X#ifdef DEBUG
- X (void) printf("%s\n", buf);
- X#endif
- X if (buf[0] == '4') /* missing article, just skipit */
- X {
- X misart++;
- X return(0);
- X }
- X
- X if (buf[0] != '2') /* uh-oh, something's wrong! */
- X {
- X#ifdef SYSLOG
- X syslog(LOG_NOTICE,"protocol error: got '%s'", buf);
- X#else
- X (void) printf("%s: protocol error: got '%s'\n", Pname, buf);
- X#endif
- X (void) close(server);
- X exit(1);
- X }
- X#ifdef DEBUG
- X (void) printf("command: %s\n", RNEWS);
- X#endif
- X if ( (inews = popen(RNEWS, "w")) == (FILE *) 0)
- X {
- X perror(RNEWS);
- X exit(1);
- X }
- X
- X /* and here comes the article, terminated with a "." */
- X#ifdef DEBUG
- X (void) printf("data\n");
- X#endif
- X while (1)
- X {
- X (void) sockread(buf);
- X if (buf[0] == '.' && buf[1] == '\0')
- X break;
- X#ifdef DEBUG
- X lines++;
- X#endif
- X (void) strcat(buf,"\n");
- X (void) fputs(((buf[0] == '.') ? buf + 1 : buf),
- X inews);
- X }
- X#ifdef DEBUG
- X (void) printf(".\n%d lines\n", lines);
- X#endif
- X (void) fflush(inews);
- X (void) pclose(inews);
- X return(0);
- X }
- X
- Xstatic jmp_buf SFGstack;
- X
- Xstatic SIGRET
- Xto_sfgets()
- X{
- X longjmp(SFGstack, 1);
- X}
- X
- Xint
- Xsockread(buf)
- Xchar *buf;
- X{
- X int esave, rz;
- X char * ret;
- X if (setjmp(SFGstack)) {
- X (void) alarm(0); /* reset alarm clock */
- X (void) signal(SIGALRM, SIG_DFL);
- X rd_fp->_flag |= _IOERR; /* set stdio error */
- X#ifndef ETIMEDOUT
- X errno = EPIPE; /* USG doesn't have ETIMEDOUT */
- X#else
- X errno = ETIMEDOUT; /* connection timed out */
- X#endif
- X#ifdef SYSLOG
- X syslog(LOG_ERR,"nntpxfer: read error on server socket: %m");
- X#else
- X (void) perror("nntpxfer: read error on server socket");
- X#endif
- X (void) close(server);
- X exit(1);
- X }
- X (void) signal(SIGALRM, to_sfgets);
- X (void) alarm(TIMEOUT);
- X ret = fgets(buf, BUFSIZ, rd_fp);
- X esave = errno;
- X (void) alarm(0); /* reset alarm clock */
- X (void) signal(SIGALRM, SIG_DFL); /* reset SIGALRM */
- X errno = esave;
- X rz = strlen(buf);
- X buf[rz-2] = '\0';
- X if (ret == (char * ) 0) {
- X#ifdef SYSLOG
- X syslog(LOG_ERR,"nntpxfer: read error on server socket: %m");
- X#else
- X (void) perror("nntpxfer: read error on server socket");
- X#endif
- X (void) fclose(rd_fp);
- X exit(1);
- X }
- X return(0);
- X}
- X
- Xsockwrite(buf)
- Xchar *buf;
- X {
- X register int sz;
- X char buf2[BUFSIZ];
- X#ifdef DEBUG
- X (void) printf(">>> %s\n", buf);
- X#endif
- X (void) strcpy(buf2,buf);
- X (void) strcat(buf2,"\r\n");
- X sz = strlen(buf2);
- X if (write(server,buf2,sz) != sz)
- X {
- X#ifdef SYSLOG
- X syslog(LOG_ERR,"nntpxfer: write error on server socket");
- X#else
- X (void) printf("nntpxfer: write error on server socket\n");
- X#endif
- X (void) close(server);
- X exit(1);
- X }
- X }
- X
- Xint
- Xwewant(articleid)
- Xchar *articleid;
- X {
- X#if defined(DBM) || defined(NDBM)
- X datum k, d;
- X#else
- X FILE *k;
- X char *histfile();
- X FILE *histfp; /* USG history file */
- X char line[BUFSIZ];
- X int len;
- X#endif
- X char id[BUFSIZ];
- X char *p;
- X
- X /* remove any case sensitivity */
- X (void) strcpy(id, articleid);
- X p = id;
- X#ifndef CNEWS
- X while (*p)
- X {
- X if (isupper(*p))
- X *p = tolower(*p);
- X p++;
- X }
- X#endif
- X#if defined(DBM) || defined(NDBM)
- X k.dptr = id;
- X k.dsize = strlen(articleid) + 1;
- X
- X#ifdef DBM
- X d = fetch(k);
- X#else
- X d = dbm_fetch(db, k);
- X#endif
- X if (d.dptr)
- X {
- X#ifdef DEBUG
- X (void) printf("dup: '%s'\n", articleid);
- X#endif
- X return(0);
- X }
- X#ifdef DEBUG
- X (void) printf("new: '%s'\n", articleid);
- X#endif
- X return(1);
- X#else
- X histfp = fopen(histfile(articleid), "r");
- X if (histfp == NULL)
- X {
- X#ifdef DEBUG
- X (void) printf("new: '%s'\n", articleid);
- X#endif
- X return(1);
- X }
- X len = strlen(articleid);
- X while (fgets(line, sizeof (line), histfp))
- X if (!strncmp(articleid, line, len))
- X break;
- X
- X if (feof(histfp)) {
- X (void) fclose(histfp);
- X#ifdef DEBUG
- X (void) printf("new: '%s'\n", articleid);
- X#endif
- X return (1);
- X }
- X (void) fclose(histfp);
- X#ifdef DEBUG
- X (void) printf("dup: '%s' %s\n", articleid,line);
- X#endif
- X return(0);
- X#endif
- X}
- X
- X#ifdef USGHIST
- X/*
- X** Generate the appropriate history subfile name
- X*/
- Xchar *
- Xhistfile(hline)
- Xchar *hline;
- X{
- X char chr; /* least significant digit of article number */
- X static char subfile[BUFSIZ];
- X
- X chr = findhfdigit(hline);
- X sprintf(subfile, "%s.d/%c", HISTORY_FILE, chr);
- X return subfile;
- X}
- X
- Xfindhfdigit(fn)
- Xchar *fn;
- X{
- X register char *p;
- X register int chr;
- X extern char * index();
- X
- X p = index(fn, '@');
- X if (p != NULL && p > fn)
- X chr = *(p - 1);
- X else
- X chr = '0';
- X if (!isdigit(chr))
- X chr = '0';
- X return chr;
- X}
- X#endif
- Xchar *
- Xerrmsg(code)
- Xint code;
- X{
- X extern int sys_nerr;
- X extern char *sys_errlist[];
- X static char ebuf[6+5+1];
- X
- X if (code > sys_nerr || code < 0) {
- X (void) sprintf(ebuf, "Error %d", code);
- X return ebuf;
- X } else
- X return sys_errlist[code];
- X}
- X
- SHAR_EOF
- if test 12837 -ne "`wc -c < 'nntpxfer.c'`"
- then
- echo shar: error transmitting "'nntpxfer.c'" '(should have been 12837 characters)'
- fi
- echo "Done with directory 'xfer'"
- cd ..
- fi
- # End of shell archive
- exit 0
-